#ifdef WIN32
#include <windows.h>
#endif

#include "Grabber.h"
#include "skydome.h"
#include "skyplane.h"
#include "Utilities.h"
#include "FPGrabber.h"
#include <boost/numeric/ublas/vector.hpp>

// Global variables section

FPGrabber * grabber = NULL;
skydome * skydomeInst = NULL;
skyplane * skyplaneInst = NULL;
Texture * testTex = NULL;
int button,state;

boost::numeric::ublas::vector<float> eye(3);
boost::numeric::ublas::vector<float> center(3);
boost::numeric::ublas::vector<float> up(3);


// Input handlers
void specialFunc(int key, int x, int y)
{
	int mod = glutGetModifiers();
	grabber->handleSpecialKey(key, mod);
}

void mouseHandler(int button, int state, int x, int y){
	button = button;
	state = state;
	grabber->handleMouse(button,state,x,y);
	glutPostRedisplay();
}

void mouseMotionHandler(int x, int y){
	grabber->handleMouse(button,state,x,y);
	glutPostRedisplay();
}

void keyboardDownHandler(unsigned char key, int x, int y){
	grabber->handleKey(key);
}

void keyboardUpHandler(unsigned char key, int x, int y){
	grabber->handleKeyUp(key);
}

// Display section

void display(){

	GLfloat lightAmbient[] =  { 1.0, 1.0, 1.0, 1.0 };
	GLfloat lightDiffuse[] =  { 0.8, 0.6, 1.0, 1.0 };
	GLfloat lightSpecular[] = { 1.0, 1.0, 1.0, 1.0 };
	GLfloat lightPos[] =      { 0.0, 0.0, 1.0, 1.0 };

	GLfloat matAmbient[] =    { 1.0, 1.0, 1.0, 1.0 };
	GLfloat matSpecular[] =   { 1.0, 1.0, 1.0, 1.0 };
	GLfloat matDiffuse[] =    { 0.6, 0.0, 0.2, 1.0 };
	GLfloat matShininess[] =  { 100.0 };


	glEnable(GL_DEPTH_TEST);

	glClearColor(0.0f, 0.0f, 0.9f, 0.0f);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	

	// Setup the light source

	glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbient);
	glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiffuse);
	glMaterialfv(GL_FRONT, GL_SHININESS, matShininess);

	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

	glEnable( GL_LIGHTING );
	glEnable( GL_LIGHT0 );
	glEnable( GL_TEXTURE_2D );

	//grabber->update();
	grabber->transform();

	//glTranslatef(0.0, 0.0, -15.0);
	
	skydomeInst->draw();
	glLoadIdentity();
	grabber->transform();
	skyplaneInst->draw();

	glFlush();
	glDisable(GL_TEXTURE_2D);
	glDisable( GL_LIGHTING );
	glDisable( GL_LIGHT0 );
	
	glutSwapBuffers();
	reportGLError(" buttom of display function", __FILE__, __LINE__);
}

void resize(int w, int h) {

	if(h == 0)
		h = 1;

	float ratio = 1.0* w / h;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45,ratio,1,1000000);
}

///////////////////////////////////////////////////////////////////////////////
//
// Main
//
///////////////////////////////////////////////////////////////////////////////

int main(int argc, char**argv){

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Skydome");
	
	glutKeyboardFunc(keyboardDownHandler);
	glutKeyboardUpFunc(keyboardUpHandler);
	glutSpecialFunc(specialFunc);
	glutMouseFunc(mouseHandler);
	glutMotionFunc(mouseMotionHandler);
	glutDisplayFunc(display);
	glutIdleFunc(display); // DO NOT USE THIS !!!
	glutReshapeFunc(resize);
	
	// Never fire gl commands before all glut callback functions have been registered

	std::cout << "Machine information: sizeof(float)=" << sizeof(float) << " | sizeof(double)=" << sizeof(double)
		      << " | sizeof(int)=" << sizeof(int) << " | sizeof(long)=" << sizeof(long) << "\n";

	reportGLError(" main 1", __FILE__, __LINE__);

	eye(0) = 0.0f; eye(1) = 15.0f; eye(2) = 30.0f;
	center(0) = 0.0f; center(1) = 0.0f; center(2) = -1.0f;
	up(0) = 0.0f; up(1) = 1.0f; up(2) = 0.0f;


	grabber = new FPGrabber(eye,center,up);
	reportGLError(" main 2", __FILE__, __LINE__);	
	skydomeInst = new skydome();
	skydomeInst->setGrabber(grabber);
	skyplaneInst = new skyplane();
	glutMainLoop();
	return 0;
}
